﻿// Copyright © Microsoft Corporation.  All Rights Reserved.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;

namespace TheOliver.Controls
{
	public class DeepZoomHelper : Behavior<MultiScaleImage>
	{
        private MultiScaleImage msi;

        double zoom = 1;
        bool duringDrag = false;
        bool mouseDown = false;
        Point lastMouseDownPos = new Point();
        Point lastMousePos = new Point();
        Point lastMouseViewPort = new Point();

        private List<MultiScaleSubImage> randomList = new List<MultiScaleSubImage>();

        int totalColumns = 0;
        int totalRows = 0;

        public double ZoomFactor
        {
            get { return zoom; }
            set { zoom = value; }
        }

		public DeepZoomHelper()
		{
		}

    	protected override void OnAttached()
        {
            base.OnAttached();

            msi = this.AssociatedObject;
            //
            // Firing an event when the MultiScaleImage is Loaded
            //
            this.msi.Loaded += new RoutedEventHandler(msi_Loaded);

            //
            // Firing an event when all of the images have been Loaded
            //
            this.msi.ImageOpenSucceeded += new RoutedEventHandler(msi_ImageOpenSucceeded);

            //
            // Handling all of the mouse and keyboard functionality
            //
            this.msi.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
            {
                lastMouseDownPos = e.GetPosition(msi);
                lastMouseViewPort = msi.ViewportOrigin;

                mouseDown = true;

                msi.CaptureMouse();
            };

            this.msi.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e)
            {
                if (!duringDrag)
                {
                    bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
                    double newzoom = zoom;

                    if (shiftDown)
                    {
                        newzoom /= 2;
                    }
                    else
                    {
                        newzoom *= 2;
                    }

                    Zoom(newzoom, msi.ElementToLogicalPoint(this.lastMousePos));
                }
                duringDrag = false;
                mouseDown = false;

                msi.ReleaseMouseCapture();
            };

            this.msi.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                lastMousePos = e.GetPosition(msi);
                if (mouseDown && !duringDrag)
                {
                    duringDrag = true;
                    double w = msi.ViewportWidth;
                    Point o = new Point(msi.ViewportOrigin.X, msi.ViewportOrigin.Y);
                    msi.UseSprings = false;
                    msi.ViewportOrigin = new Point(o.X, o.Y);
                    msi.ViewportWidth = w;
                    zoom = 1 / w;
                    msi.UseSprings = true;
                }

                if (duringDrag)
                {
                    Point newPoint = lastMouseViewPort;
                    newPoint.X += (lastMouseDownPos.X - lastMousePos.X) / msi.ActualWidth * msi.ViewportWidth;
                    newPoint.Y += (lastMouseDownPos.Y - lastMousePos.Y) / msi.ActualWidth * msi.ViewportWidth;
                    msi.ViewportOrigin = newPoint;
                }
            };

            this.msi.MouseWheel += delegate(object sender, System.Windows.Input.MouseWheelEventArgs e)
            {
                e.Handled = true;

                double newzoom = zoom;

                if (e.Delta < 0)
                    newzoom /= 1.3;
                else
                    newzoom *= 1.3;

                Zoom(newzoom, msi.ElementToLogicalPoint(this.lastMousePos));
                msi.CaptureMouse();
            };
        }

        void msi_Loaded(object sender, RoutedEventArgs e)
        {
            // Hook up any events you want when the image has successfully been opened
        }

        private void Zoom(double newzoom, Point p)
        {
            msi.ZoomAboutLogicalPoint(newzoom / zoom, p.X, p.Y);
            zoom = newzoom;
        }

        private void ZoomInClick(object sender, System.Windows.RoutedEventArgs e)
        {
            Zoom(zoom * 1.3, msi.ElementToLogicalPoint(new Point(.5 * msi.ActualWidth, .5 * msi.ActualHeight)));
        }

        private void ZoomOutClick(object sender, System.Windows.RoutedEventArgs e)
        {
            Zoom(zoom / 1.3, msi.ElementToLogicalPoint(new Point(.5 * msi.ActualWidth, .5 * msi.ActualHeight)));
        }

        private void GoHomeClick(object sender, System.Windows.RoutedEventArgs e)
        {
            this.msi.ViewportWidth = 1;
            this.msi.ViewportOrigin = new Point(0, 0);
            ZoomFactor = 1;
        }

        private void GoFullScreenClick(object sender, System.Windows.RoutedEventArgs e)
        {
            if (!Application.Current.Host.Content.IsFullScreen)
            {
                Application.Current.Host.Content.IsFullScreen = true;
            }
            else
            {
                Application.Current.Host.Content.IsFullScreen = false;
            }
        }

        /*
        // Handling the VSM states
        private void LeaveMovie(object sender, System.Windows.Input.MouseEventArgs e)
        {
            VisualStateManager.GoToState(this, "FadeOut", true);
        }

        private void EnterMovie(object sender, System.Windows.Input.MouseEventArgs e)
        {
            VisualStateManager.GoToState(this, "FadeIn", true);
        }
         * */


        // unused functions that show the inner math of Deep Zoom
        public Rect getImageRect()
        {
            return new Rect(-msi.ViewportOrigin.X / msi.ViewportWidth, -msi.ViewportOrigin.Y / msi.ViewportWidth, 1 / msi.ViewportWidth, 1 / msi.ViewportWidth * msi.AspectRatio);
        }

        public Rect ZoomAboutPoint(Rect img, double zAmount, Point pt)
        {
            return new Rect(pt.X + (img.X - pt.X) / zAmount, pt.Y + (img.Y - pt.Y) / zAmount, img.Width / zAmount, img.Height / zAmount);
        }

        public void LayoutDZI(Rect rect)
        {
            double ar = msi.AspectRatio;
            msi.ViewportWidth = 1 / rect.Width;
            msi.ViewportOrigin = new Point(-rect.Left / rect.Width, -rect.Top / rect.Width);
        }

        void msi_ImageOpenSucceeded(object sender, RoutedEventArgs e)
        {
            //If collection, this gets you a list of all of the MultiScaleSubImages
            //
            //foreach (MultiScaleSubImage subImage in msi.SubImages)
            //{
            //    // Do something
            //}

            ArrangeIntoGrid();

            //msi.ViewportWidth = 1;
        }

        private List<MultiScaleSubImage> RandomizedListOfImages()
        {
            List<MultiScaleSubImage> imageList = new List<MultiScaleSubImage>();
            Random ranNum = new Random();

            // Store List of Images
            foreach (MultiScaleSubImage subImage in msi.SubImages)
            {
                subImage.Opacity = 1;
                imageList.Add(subImage);
            }

            int numImages = imageList.Count;

            // Randomize Image List
            for (int a = 0; a < numImages; a++)
            {
                MultiScaleSubImage tempImage = imageList[a];
                imageList.RemoveAt(a);

                int ranNumSelect = ranNum.Next(imageList.Count);

                imageList.Insert(ranNumSelect, tempImage);
            }

            return imageList;
        }

        private void ArrangeIntoGrid()
        {
            randomList = RandomizedListOfImages();

            int totalImagesAdded = 0;
            double totalWidth = 0;
            double xoffset = 1.25;
            double yoffset = 1.25;
            double margin = .1;

            if (randomList.Count > 1)
            {
                int numberOfImages = randomList.Count;
                totalColumns = 0;

                totalColumns = (int)Math.Ceiling(Math.Sqrt(numberOfImages));

                totalRows = numberOfImages / (totalColumns - 1);
                //MessageBox.Show(totalRows.ToString() + " " + totalColumns.ToString());

                totalWidth = totalColumns * xoffset;

                // Set up the animation to layout in grid
                Storyboard moveStoryboard = new Storyboard();

                for (int row = 0; row < totalRows; row++)
                {
                    for (int col = 0; col < totalColumns; col++)
                    {
                        if (numberOfImages != totalImagesAdded)
                        {
                            MultiScaleSubImage currentImage = randomList[totalImagesAdded];
                            currentImage.ViewportWidth = 1;

                            /*
                            if (currentImage.AspectRatio <= 1)
                            {
                                currentImage.ViewportWidth = 2;
                            }
                            else
                            {
                                currentImage.ViewportWidth = 1;
                            }*/

                            Point currentPosition = new Point(0, 0);
                            Point futurePosition = new Point(-xoffset * col, -yoffset * row);

                            // Create Animation
                            PointAnimationUsingKeyFrames moveAnimation = new PointAnimationUsingKeyFrames();

                            // Create Keyframe
                            SplinePointKeyFrame startKeyframe = new SplinePointKeyFrame();
                            startKeyframe.Value = currentPosition;
                            startKeyframe.KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero);

                            startKeyframe = new SplinePointKeyFrame();
                            startKeyframe.Value = futurePosition;
                            startKeyframe.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100));

                            KeySpline ks = new KeySpline();
                            ks.ControlPoint1 = new Point(0, 1);
                            ks.ControlPoint2 = new Point(1, 1);
                            startKeyframe.KeySpline = ks;
                            moveAnimation.KeyFrames.Add(startKeyframe);

                            Storyboard.SetTarget(moveAnimation, currentImage);

                            Storyboard.SetTargetProperty(moveAnimation, new PropertyPath("ViewportOrigin"));

                            moveStoryboard.Children.Add(moveAnimation);

                            totalImagesAdded++;
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                if (!msi.Resources.Contains("myAnim"))
                {
                    msi.Resources.Add("myAnim", moveStoryboard);
                }

                this.msi.ViewportOrigin = new Point(-margin, -margin);
                this.msi.ViewportWidth = totalWidth + margin;

                // Play Storyboard
                moveStoryboard.Begin();
            }
            else
            {
                MultiScaleSubImage currentImage = randomList[0];
                currentImage.ViewportWidth = 1 + 2 * margin;
                currentImage.ViewportOrigin = new Point(0, 0);
                this.msi.ViewportOrigin = new Point(-margin, -margin);
                this.msi.ViewportWidth = 1;
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Insert code that you would want run when the Behavior is removed from an object.
        }

        /*
        public ICommand MyCommand
        {
            get;
            private set;
        }
         
        private void MyFunction()
        {
            // Insert code that defines what the behavior will do when invoked.
        }
         * */
    }
}